import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:sample2/helper/app_helper.dart';
import 'package:sample2/model/chat/chat_group_message.dart';
import 'package:sample2/model/chat/chat_user.dart';
import 'package:sample2/model/chat/message.dart';
import 'package:sample2/model/chat/chat_constants.dart';
import 'package:sample2/utils/local_storage.dart';
import 'package:socket_io_client/socket_io_client.dart' as io;

typedef ChatMessageCallback = void Function(ChatMessage);
typedef ChatErrorCallback = void Function(int, String);
typedef ChatMessageSuccessCallback = void Function(String);

class ChatHelper {
  static io.Socket? socket;
  static VoidCallback? _newCommerCallback;
  static final HashMap<int, ChatMessageCallback> _newMessageCallbacks =
      HashMap();
  static List<ChatMessageUser> _users = [];
  static ChatUser? _currentUser;
  static ChatUser? _currentToUser;
  static List<ChatMessage>? _chatMessages;

  static List<ChatMessageUser> getUsers() {
    return _users;
  }

  static void updateUsers(List<ChatMessageUser> us) {
    _users = us;
  }

  static void appendChatMessages(ChatMessage message) {
    _chatMessages ??= [];
    _chatMessages!.add(message);
    ChatMessageUser? user =
        _users.where((element) => element.chatId == message.from).first;
    user.lastMessage = message.message;
  }

  static void appendGroupChatMessage(ChatMessage message) {
    _chatMessages ??= [];
    _chatMessages!.add(message);
  }

  static Future<ChatUser?> getCurrentUser() async {
    String? username = (await AppHelper.shared.getUserInfo())?.username;
    if (_currentUser != null && _currentUser!.userName == username) {
      return _currentUser!;
    }
    if (_users.isNotEmpty) {
      _currentUser = _users.where((ChatUser cu) {
        return cu.userName == username;
      }).first;
    }
    return _currentUser;
  }

  static void updateCurrentToUser(ChatUser toUser) {
    _currentToUser = toUser;
  }

  static void releaseToUser() {
    _currentToUser = null;
  }

  static void addChangeUserListener(VoidCallback callback) {
    _newCommerCallback = callback;
  }

  static int addNewMessageListener(ChatMessageCallback callback) {
    int key = DateTime.now().microsecondsSinceEpoch;
    _newMessageCallbacks.putIfAbsent(key, () => callback);
    return key;
  }

  static void removeNewMessageListener(int? handler) {
    if (handler == null) {
      return;
    }
    _newMessageCallbacks.remove(handler);
  }

  static void initialAndConnectToChatServer(
      VoidCallback? onConnect, ChatErrorCallback? onError) async {
    var auth = {
      "username": (await AppHelper.shared.getUserInfo())?.username ?? "",
      "userid": (await AppHelper.shared.getUserInfo())?.userid ?? "",
      "authorization": (await AppHelper.shared.getUserInfo())?.token ?? ""
    };

    io.Socket socket = io.io(
        AppHelper.shared.getSocketIoAddress(),
        io.OptionBuilder()
            .setPath(AppHelper.shared.getChatPath())
            .setTransports(['websocket'])
            .disableAutoConnect()
            .setExtraHeaders({"Origin": "localhost"})
            .build());
    socket.auth = auth;
    socket.onConnect((data) {
      int index = _users.indexWhere(
          (ChatUser user) => user.userId == AppHelper.shared.userInfo?.userid);
      if (index >= 0) {
        ChatUser currentUser = _users[index];
        currentUser.status = ChatPeerStatus.online;
      }
      onConnect?.call();
      debugPrint("connect");
      socket.emitWithAck("chat/client", "test", ack: (respdata) {
        debugPrint("got server data : $respdata");
      });
      socket.on("chat/newUser", (respdata) {
        debugPrint(respdata.toString());
        if (respdata != null) {
          Map aUser = respdata;
          if (!_users
              .any((user) => user.userId == aUser["userid"].toString())) {
            _users.add(ChatMessageUser(
                userName: aUser["username"].toString(),
                userId: aUser["userid"].toString(),
                time: aUser["time"].toString(),
                chatId: aUser["chatid"].toString(),
                status: ChatPeerStatus.values[aUser["status"]]));
          }
        }
        _newCommerCallback?.call();
        debugPrint("----new user ---");
      });
      socket.on("chat/private", (data) {
        var rldata = data;
        if (data is List) {
          rldata = data.first;
          var ack = data.last;
          //给server端反馈已经接收到数据
          ack("received");
        }
        //TODO: add cache message function.
        ChatMessage newChatMsg = ChatMessage.fromJsonMap(rldata);
        ChatHelper.appendChatMessages(newChatMsg);
        for (var cb in _newMessageCallbacks.values) {
          cb.call(newChatMsg);
        }
      });

      socket.on("chat/group", (data) {
        var rldata = data;
        if (data is List) {
          rldata = data.first;
          var ack = data.last;
          //给server端反馈已经接收到数据
          ack("received");
        }
        //TODO: add cache message function.
        ChatGroupMessage newChatMsg = ChatGroupMessage.fromJsonMap(rldata);
        ChatHelper.appendGroupChatMessage(newChatMsg);
        for (var cb in _newMessageCallbacks.values) {
          cb.call(newChatMsg);
        }
      });
      socket.on("chat/user/status", (aUser) {
        ChatMessageUser updatedChatUser = ChatMessageUser(
            userName: aUser["username"].toString(),
            userId: aUser["userid"].toString(),
            time: aUser["time"].toString(),
            chatId: aUser["chatid"].toString(),
            status: ChatPeerStatus.values[aUser["status"]]);
        int findIndex = _users.indexWhere((u) {
          return u.userId == aUser["userid"].toString();
        });
        if (findIndex >= 0) {
          _users[findIndex] = updatedChatUser;
          _newCommerCallback?.call();
        }
      });

      socket.onDisconnect((respdata) {
        debugPrint("disconnect");
      });
      socket.onAny((event, respdata) {
        debugPrint("got data from server: $respdata");
      });
    });
    socket.onError((err) {
      onError?.call(-1, err.toString());
      debugPrint("got error ${err.toString()}");
    });
    socket.connect();
    ChatHelper.socket = socket;
  }

  //send a message to a peer
  static sendMessage(String peerId, ChatMessage chatMessage,
      ChatMessageSuccessCallback callback) {
    ChatHelper.socket?.emitWithAck("chat/private", chatMessage.toParameter(),
        ack: (Map respData) {
      callback.call(respData["msgId"]);
      debugPrint("sent message success and got messageId $respData");
    });
  }

  static joinGroup(
      String groupId, VoidCallback onSuccess, ChatErrorCallback onError) {
    ChatHelper.socket?.emitWithAck("chat/group/in", {"groupId": groupId},
        ack: (respData) {
      onSuccess();
      debugPrint("join group $groupId Success $respData");
    });
  }

  //send a message to a group
  static sendMessageToGroup(String groupId, ChatMessage chatMessage,
      ChatMessageSuccessCallback callback) {
    ChatHelper.socket?.emitWithAck("chat/group", chatMessage.toParameter(),
        ack: (Map respData) {
      callback.call(respData["msgId"]);
      debugPrint("sent group message success and got messageId $respData");
    });
  }

  static saveAndCleanCurrentMessage() {
    LocalStorage.getShared().then((shared) {
      List<ChatMessage>? messages = ChatHelper._chatMessages;
      const messageHistoryKey = "com.test.message.history";
      String? messageHistory = "[]";
      //TODO:
      _chatMessages = null;
    });
  }

  // add a peer to contact list
  static addPeerToContactList(String peerId) {}
  //create a anonymous group that support all members in this group anonymous
  static createAnonymousGroup(String groupDesc, String? tag) {}
  //join a group with a self-define name alias
  static enterAnonymousGroup(String groupId, String participatorAlias) {}
  //send an anonymous group
  static sendMessageToAnonymousGroup(String groupId, ChatMessage chatMessage) {}
  static disconnect() {
    socket?.disconnect();
  }
}
